﻿
/*
** 2D Game Animation XNA api 
** by Pakholkov Leonid am98pln@gmail.com
**
**
** Класс подвижного спрайта - "сердце" моего формата анимации
** 
**
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
//using TestProject.MotionValues;
//using TestProject.Animation;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Game2DAnimationAPI 
{
    public struct FPoint
    {
        public float x;
        public float y;
        public FPoint(float X, float Y)
        {
            x = X;
            y = Y;
        }
    }

    public class Quad
    {
        public FPoint[] uv;
        public FPoint[] v;
        public Quad()
        { 
            uv = new FPoint[4];
            v = new FPoint[4];
        }
    }

    public class MovingPart
    {
        
        static public float Read(XElement xe, string name, float defaultValue) {
            if (xe.Attribute(name) == null)
            {
                return defaultValue;
            }
            string tmp = xe.Attribute(name).Value.Replace('.', ',');
	        return (float)Convert.ToDouble(tmp);
        }
        
	    //
	    // Конструктор - читаем данные спрайта из XML
	    // запоминаем текстуру, спрайты из которой рисуем
	    //
        public MovingPart(Animation animation, XElement xe, int sizeX, int sizeY)
        { 
            animation.AddBone(this);

            var boneParams = xe.Attributes().ToList();

            boneName =  xe.Attribute("name").Value;

	        if (xe.Attribute("moving_type") == null || xe.Attribute("moving_type").Value == "spline") {
		        _movingType = MotionValues.Motion.m_spline;
	        } else if (xe.Attribute("moving_type").Value == "line") {
		        _movingType = MotionValues.Motion.m_line;
	        } else {
		        _movingType = MotionValues.Motion.m_discontinuous;
	        }
	        _order = Convert.ToInt32(xe.Attribute("order").Value);
	        _center.x = Read(xe, "centerX", 0);
            _center.y = Read(xe, "centerY", 0);
            if (_hasTexture = (xe.Attribute("texture") != null))
            {
                CreateQuad(sizeX, sizeY, xe.Attribute("texture").Value);
	        }

	        //
	        // вот это очень важный блок - тут читаются данные 
	        // о положении подвижного спрайта на экране и его движении 
	        // во времени
	        //
            var partsList = xe.Elements("pos").ToList();
   	        if (partsList.Count() == 0) {
		        _x.AddKey(0, 0);
		        _y.AddKey(0, 0);
		        _scaleX.AddKey(0, 1);
		        _scaleY.AddKey(0, 1);
		        _angle.AddKey(0, 0);
		        _alpha.AddKey(0, 1);
	        }
	        foreach (var pos in partsList) 
            {
		        float time = Read(pos, "time", 0);
		        _x.AddKey(time, Read(pos, "x", 0));
                _y.AddKey(time, Read(pos, "y", 0));	
                _scaleX.AddKey(time, Read(pos, "scaleX", 1));
                _scaleY.AddKey(time, Read(pos, "scaleY", 1));
                _angle.AddKey(time, Read(pos, "angle", 0));
                _alpha.AddKey(time, Read(pos, "alpha", 1));
	        }
	        _x.SetType(_movingType);
	        _y.SetType(_movingType);	
	        _scaleX.SetType(_movingType);
	        _scaleY.SetType(_movingType);
	        _angle.SetType(_movingType);
	        _alpha.SetType(MotionValues.Motion.m_line);

	        // 
	        // если есть - создаем дочерние подвижные спрайты
	        //
	        partsList = xe.Elements("movingPart").ToList();
	        foreach (var element in partsList) {
                _bones.Add(new MovingPart(animation, element, sizeX, sizeY));
	        }
        }

	    //
	    // Выводим спрайт на экран
	    // положение во времени анимации - p, [ 0<= p <= 1 ]
	    // положение на экране - stack
	    //
        public void PreDraw(float p, Matrix stack)
        { 
	        float[] localT = new float[1];
	        int index = _x.Value(p, localT);

	        // если функция определена для значения "p" - рисуем
	        // тут проверка только для "_x" - т.к. у остальных функций движения область определения такая же
	        if (_visible = (index >= 0)) {
		        Matrix matrix = new Matrix(stack);
		        matrix.Move(_x.GetFrame(index, localT[0]), _y.GetFrame(index, localT[0]));
		        matrix.Rotate(_angle.GetFrame(index, localT[0]));
		        matrix.Scale(_scaleX.GetFrame(index, localT[0]), _scaleY.GetFrame(index, localT[0]));

		        matrix.Move(-_center.x, -_center.y);

		        _alphaCurrent = _alpha.GetFrame(index, localT[0]);

		        if (_hasTexture) 
		        {
                    float[] x = new float[1];
                    float[] y = new float[1];
                    matrix.Mul(_origin[0].x, _origin[0].y, x, y);
                    _quad.v[0].x = x[0];
                    _quad.v[0].y = y[0];
                    matrix.Mul(_origin[1].x, _origin[1].y, x, y);
                    _quad.v[1].x = x[0];
                    _quad.v[1].y = y[0];
                    matrix.Mul(_origin[2].x, _origin[2].y, x, y);
                    _quad.v[2].x = x[0];
                    _quad.v[2].y = y[0];
                    matrix.Mul(_origin[3].x, _origin[3].y, x, y);
                    _quad.v[3].x = x[0];
                    _quad.v[3].y = y[0];
		        }        

		        for (int i = 0; i < _bones.Count(); ++i)
		        {
			        _bones[i].PreDraw(p, matrix);
		        }
	        }
        }

	    public void Draw(VertexPositionColorTexture[] vb, int[] quadCounter) {
	        if (_visible && _hasTexture) {
    	        //Render::DrawQuad(_quad);
                int index = 4 * quadCounter[0];
                vb[index + 0].Position = new Vector3(_quad.v[0].x, _quad.v[0].y, 0);
                vb[index + 0].TextureCoordinate = new Vector2(_quad.uv[0].x, _quad.uv[0].y);
                vb[index + 1].Position = new Vector3(_quad.v[1].x, _quad.v[1].y, 0);
                vb[index + 1].TextureCoordinate = new Vector2(_quad.uv[1].x, _quad.uv[1].y);
                vb[index + 2].Position = new Vector3(_quad.v[2].x, _quad.v[2].y, 0);
                vb[index + 2].TextureCoordinate = new Vector2(_quad.uv[2].x, _quad.uv[2].y);
                vb[index + 3].Position = new Vector3(_quad.v[3].x, _quad.v[3].y, 0);
                vb[index + 3].TextureCoordinate = new Vector2(_quad.uv[3].x, _quad.uv[3].y);
                vb[index + 0].Color = vb[index + 1].Color = vb[index + 2].Color = vb[index + 3].Color = Color.White;
                quadCounter[0]++;
            }
        }

	    string boneName;
	
	    FPoint _center;
	    MotionValues _x = new MotionValues();
        MotionValues _y = new MotionValues();
        MotionValues _angle = new MotionValues();
        MotionValues _alpha = new MotionValues();
	    float _alphaCurrent;
        MotionValues _scaleX = new MotionValues();
        MotionValues _scaleY = new MotionValues();
	    MotionValues.Motion _movingType;

	    List<MovingPart> _bones = new List<MovingPart>();
	    int _order;
        public int Order() { return _order; }
        public bool _visible;

	    //
	    //
        public void CreateQuad(int sizeX, int sizeY, string texture)
        {
	        float width = (float)(_width = sizeX);
	        float height = (float)(_height = sizeY);
	        // конец блока

	        int start = 0;
	        int end = texture.IndexOf(':', start);
	        float x = (float)Convert.ToDouble(texture.Substring(start, end - start));
	        start = end + 1;
            end = texture.IndexOf(':', start);
            float y = (float)Convert.ToDouble(texture.Substring(start, end - start));
	        start = end + 1;
            end = texture.IndexOf(':', start);
            float w = (float)Convert.ToDouble(texture.Substring(start, end - start));
	        start = end + 1;
            end = texture.IndexOf(':', start);
            float h = (float)Convert.ToDouble(texture.Substring(start, end - start));
	        start = end + 1;
            end = texture.IndexOf(':', start);
            float offX = (float)Convert.ToDouble(texture.Substring(start, end - start));
	        start = end + 1;
            float offY = (float)Convert.ToDouble(texture.Substring(start));

	        // начало блока - этот код зависит от используемого движка
	        //_quad.color[0] = 0xFFFFFFFF;
	        _quad.uv[0].x = x / width; _quad.uv[0].y = y / height;
	        //_quad.color[1] = 0xFFFFFFFF;
	        _quad.uv[1].x = (x + w) / width; _quad.uv[1].y = y / height;
	        //_quad.color[2] = 0xFFFFFFFF;
	        _quad.uv[2].x = (x + w) / width; _quad.uv[2].y = (y + h) / height;
	        //_quad.color[3] = 0xFFFFFFFF;
	        _quad.uv[3].x = x / width; _quad.uv[3].y = (y + h) / height;
	        // конец блока

	        x = offX;
	        y = offY;
	        _origin[0].x = x;     _origin[0].y = y;
	        _origin[1].x = x + w;     _origin[1].y = y;
	        _origin[2].x = x + w; _origin[2].y = y + h;
	        _origin[3].x = x; _origin[3].y = y + h;

        }

	    bool _hasTexture;
	    Quad _quad = new Quad();

	    FPoint[] _origin = new FPoint[4];	
	    int _width;
	    int _height;

        public bool CmpBoneOrder(MovingPart one, MovingPart two)
        {
            return one._order < two._order;
        }

        public bool HasTexture() { return _hasTexture; }
    
    };

}